package org.example.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.aspectj.weaver.loadtime.Aj;
import org.example.entity.AjaxResult;
import org.example.security.TokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true) // 启用控制器上面的权限注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource(name = "UserDetailsServiceImpl")
    private UserDetailsService userDetailsServiceImpl;
    @Autowired
    private TokenFilter tokenFilter;

    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    /**
     * 指明使用userDetailsServiceImpl类方法去查询用户和权限
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsServiceImpl);
    }

//    排除静态资源，无需权限认证
//    @Override
//    public void configure(WebSecurity web) throws Exception {
//        super.configure(web);
//    }

    /**
     * 授权
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable(); // 关闭csrf,因为是前后端分离项目，无需通过cookie传递session id
        http.authorizeRequests().antMatchers("/login","/logout") .anonymous().anyRequest().authenticated();// 授权配置
//        http.exceptionHandling().authenticationEntryPoint((request, response, e) ->{
//            toJson(response,401,"登录失败"); // 登录失败的异常处理
//        }).accessDeniedHandler(((httpServletRequest, httpServletResponse, e) -> {
//            toJson(httpServletResponse,403,"无权访问"+httpServletRequest.getRequestURI());
//        })) ;// 访问没有权限的表达式
        // 在执行鉴权过滤器UsernamePasswordAuthenticationFilter之前先执行tokenFilter
        http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class); // 在验证名字密码之前调用过滤器，如果传了token，
        // 就把名字密码从redis找回来了，不用查数据库了
    }

//    @SneakyThrows
//    private void toJson(HttpServletResponse response, int code, String msg) {
//        AjaxResult err=AjaxResult.error(code,msg);
//        response.setContentType("application/json;charset=UTF-8");
//        response.getWriter().write(new ObjectMapper().writeValueAsString(err));
//        response.getWriter().close();
//    }

    /**
     * 密码加密方式
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder(); // 加盐加密
    }

    public static void main(String[] args) {
        BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
        System.out.println("passwordEncoder.encode(\"123\") = " + passwordEncoder.encode("123"));
        System.out.println("passwordEncoder.encode(\"123\") = " + passwordEncoder.encode("123"));
    }
}
